Current File : /home/jeconsul/public_html/wp-content/plugins/suremails/src/components/attachments/attachments.js
import React, { useEffect, useState } from 'react';
import { Download, FileText } from 'lucide-react';
import { __ } from '@wordpress/i18n';

export const AttachmentList = ( { attachments } ) => {
	// Helper function to return the full attachment URL.
	const getAttachmentUrl = ( attachment ) => {
		const attachment_url = window.suremails?.attachmentUrl;
		return `${ attachment_url }${ attachment }`;
	};

	// State to store file sizes and missing file information
	const [ fileSizes, setFileSizes ] = useState( {} );

	// Function to fetch file sizes and check if file exists
	const fetchFileSize = async ( attachment ) => {
		try {
			const url = getAttachmentUrl( attachment );
			let response = await fetch( url, { method: 'HEAD' } );

			if ( ! response.ok ) {
				setFileSizes( ( prev ) => ( {
					...prev,
					[ attachment ]: 'No File Found',
				} ) );
				return;
			}

			let size = response.headers.get( 'content-length' );

			if ( ! size ) {
				response = await fetch( url, {
					method: 'GET',
					headers: { Range: 'bytes=0-0' },
				} );
				size =
					response.headers
						.get( 'content-range' )
						?.split( '/' )[ 1 ] || null;
			}

			setFileSizes( ( prev ) => ( {
				...prev,
				[ attachment ]: size ? formatFileSize( size ) : 'No File Found',
			} ) );
		} catch ( error ) {
			setFileSizes( ( prev ) => ( {
				...prev,
				[ attachment ]: 'No File Found',
			} ) );
		}
	};

	// Format file size (bytes to KB, MB, etc.)
	const formatFileSize = ( bytes ) => {
		const sizes = [ 'Bytes', 'KB', 'MB', 'GB', 'TB' ];
		if ( ! bytes || bytes === 0 ) {
			return '0 Byte';
		}
		const i = Math.floor( Math.log( bytes ) / Math.log( 1024 ) );
		return `${ ( bytes / Math.pow( 1024, i ) ).toFixed( 2 ) } ${
			sizes[ i ]
		}`;
	};

	// Fetch file sizes when component mounts
	useEffect( () => {
		attachments.forEach( fetchFileSize );
	}, [ attachments ] );

	return (
		<div className="flex p-6 bg-background-secondary rounded-sm">
			{ attachments.length > 0 ? (
				<ul className="w-full space-y-4">
					{ attachments.map( ( attachment, index ) => {
						const { isImage, displayName } =
							isImageFile( attachment );
						const fileSizeText =
							fileSizes[ attachment ] || 'Fetching size...';
						const fileExists = fileSizeText !== 'No File Found';
						const attachmentUrl = getAttachmentUrl( attachment );

						return (
							<li
								key={ index }
								className="flex items-center space-x-3 py-3 px-3 rounded-md bg-background-primary first:-mt-[13px] last:-mb-[13px] hover:bg-blue-50 transition"
							>
								{ /* File preview wrapped in a link to open the full image. */ }
								<div className="w-10 h-10 flex-shrink-0 rounded-md overflow-hidden bg-background-primary flex items-center justify-center">
									{ isImage && fileExists ? (
										<a
											href={ add_timestamp(
												attachmentUrl
											) }
											target="_blank"
											rel="noopener noreferrer"
										>
											<img
												src={ attachmentUrl }
												alt={
													displayName ??
													__(
														'Attachment',
														'suremails'
													)
												}
												className="w-full h-full object-contain transform transition-transform duration-300 hover:scale-110"
											/>
										</a>
									) : (
										<FileText className="w-8 h-8 text-field-helper" />
									) }
								</div>

								{ /* File details (Name + Size) */ }
								<div className="flex-1 gap-1">
									<a
										href={
											fileExists
												? add_timestamp( attachmentUrl )
												: '#'
										}
										target="_blank"
										rel="noopener noreferrer"
										className={ `text-field-label text-sm no-underline border-none ml-3 focus:outline-none focus:ring-0 ${
											fileExists
												? ''
												: 'pointer-events-none text-field-label'
										}` }
									>
										{ displayName }
									</a>
									<p className="text-xs text-field-helper ml-3">
										{ fileSizeText }
									</p>
								</div>

								{ /* Download button (only if file exists) */ }
								{ fileExists && (
									<a
										href={ attachmentUrl }
										download={ displayName }
										className="bg-background-primary no-underline border-none hover:bg-blue-50"
										title={ __( 'Download', 'suremails' ) }
									>
										<Download className="w-4 h-4 text-text-secondary" />
									</a>
								) }
							</li>
						);
					} ) }
				</ul>
			) : (
				<p className="text-sm text-field-helper">No attachments.</p>
			) }
		</div>
	);
};

// Helper function to determine if a file is an image
const isImageFile = ( filename ) => {
	const imageExtensions = [ 'jpg', 'jpeg', 'png', 'gif', 'webp' ];
	const ext = filename.split( '.' ).pop().toLowerCase();
	const isImage = imageExtensions.includes( ext );
	const displayName = filename.substring( filename.indexOf( '-' ) + 1 );
	return { isImage, displayName };
};

/**
 *
 * @param {string} url URL to add timestamp to for cache busting purposes. This is used to ensure the image is reloaded when the user clicks on it.
 * @return {string} URL with timestamp appended. If no URL is provided, an empty string is returned.
 */
const add_timestamp = ( url ) => {
	if ( url ) {
		return url + `?=` + new Date().getTime();
	}
};